#include <stdio.h>
#include <cpu.h>
#include <interrupts.h>
#include <plat.h>
#include <objcache.h>

typedef struct {
    node_t node;
    cpu_msg_t msg;
} cpu_msg_node_t;

static cpuif_t cpu_interface[PLAT_CORE_COUNT] __aligned(PAGE_SIZE);

static inline cpuif_t* cpu_if(uint64_t cpu_id)
{
    return &cpu_interface[cpu_id];
}

cpu_synctoken_t cpu_glb_sync = {.ready = false};

objcache_t msg_cache;

/* 从linker.ld中导入变量,获得section的位置 */
IMPORT_SYM(unsigned long, __ipi_cpumsg_handlers_start__, ipi_cpumsg_handlers_start);
IMPORT_SYM(unsigned long, __ipi_cpumsg_handlers_end__, ipi_cpumsg_handlers_end);
IMPORT_SYM(uintptr_t, __ipi_cpumsg_handlers_id_start__, ipi_cpumsg_handlers_id_start);

cpu_msg_handler_t *ipi_cpumsg_handlers = (cpu_msg_handler_t *)ipi_cpumsg_handlers_start;
uint64_t ipi_cpumsg_handler_num;

void cpu_init(void)
{
    cpu_arch_init();

    if(cpu_is_master()){
        cpu_sync_init(&cpu_glb_sync, PLAT_CORE_COUNT);
        objcache_init(&msg_cache, sizeof(cpu_msg_node_t), false);

        ipi_cpumsg_handler_num = ((ipi_cpumsg_handlers_end - ipi_cpumsg_handlers_start) \
                                    / sizeof(cpu_msg_handler_t));
        /*
         * 给通过DECLARE_CPU_MSG_HANDLER()注册的每一个函数进行编号
         * 以便cpu_get_msg()时可以快速索引
         */
        for (int i = 0; i < ipi_cpumsg_handler_num; i++) {
            ((uintptr_t *)ipi_cpumsg_handlers_id_start)[i] = i;
        }

        boot_secondarys();
    }

    /* 所有CPU在此执行汇合 */
    cpu_sync_barrier(&cpu_glb_sync);
}

void cpu_idle(void)
{
    cpu_arch_idle();

    /**
     * Should not return here.
     */
    printf("Spurious idle wake up\n");
}

void cpu_send_msg(uint64_t trgtcpu, cpu_msg_t *msg)
{
    /*
     * 不用过多纠结这里的objcache_alloc(),就是相当于一个malloc()
     * 因为想提升点效率，就参考了slab机制做了个缓存
     */
    cpu_msg_node_t *node = objcache_alloc(&msg_cache);
    if (node == NULL)
        printf("cant allocate msg node\n");

    node->msg = *msg;
    list_push(&cpu_if(trgtcpu)->event_list, (node_t *)node);
    fence_sync_write();
    interrupts_cpu_sendipi(trgtcpu, IPI_CPU_MSG);
}

static bool cpu_get_msg(cpu_msg_t *msg)
{
    cpu_msg_node_t *node = NULL;
    if ((node = (cpu_msg_node_t *)list_pop(&cpu_if(get_cpuid())->event_list)) !=
        NULL) {
        *msg = node->msg;
        objcache_free(&msg_cache, node);
        return true;
    }
    return false;
}

void cpu_msg_handler(unsigned int irq)
{
    cpu_msg_t msg;
    while (cpu_get_msg(&msg)) {
        if (msg.handler < ipi_cpumsg_handler_num && \
            ipi_cpumsg_handlers[msg.handler]) {
            ipi_cpumsg_handlers[msg.handler](msg.event, msg.data);
        }
    }
}

